import javascript
import semmle.javascript.security.dataflow.ReflectedXss::ReflectedXss

/**
 * A function with `req` and `res` parameters, and hence most likely an
 * HTTP route handler.
 */
class LikelyRouteHandler extends DataFlow::FunctionNode {
  DataFlow::ParameterNode req;
  DataFlow::ParameterNode res;

  LikelyRouteHandler() {
    req = getParameter(0) and req.getName() = "req" and
    res = getParameter(1) and res.getName() = "res"
  }

  DataFlow::ParameterNode getRequestParameter() {
    result = req
  }

  /** Gets a method of `res` that sends an HTTP response. */
  string getASendMethodName() {
    // res.send
    result = "send"
    or
    // or a method `m` such that there is an assignment `res.m = res.n` where `n`
    // is already known to be a send method
    exists (DataFlow::PropWrite pwn |
      pwn = res.getAPropertyWrite(result) and
      pwn.getRhs() = getASendMethodReference()
    )
  }

  /** Gets a reference to `res.send` or some other known send method. */
  DataFlow::PropRead getASendMethodReference() {
    result = res.getAPropertyRead(getASendMethodName())
  }

  /** Gets a call to the send method. */
  DataFlow::CallNode getASendMethodCall() {
    result = getASendMethodReference().getACall()
  }
}

/** An argument passed to `res.send`, marked as an XSS sink. */
class LikelySendArgument extends Sink {
  LikelySendArgument() {
    this = any(LikelyRouteHandler rh).getASendMethodCall().getAnArgument()
  }
}

/** An access to a request parameter, marked as an XSS source. */
class LikelyRequestParameter extends Source, DataFlow::SourceNode {
  LikelyRequestParameter() {
    exists (DataFlow::SourceNode base | this = base.getAPropertyRead() |
      // either a property access on `req` itself
      base = any(LikelyRouteHandler rh).getRequestParameter()
      or
      // or a more deeply nested property access
      base instanceof LikelyRequestParameter
    )
  }
}

// We will treat the parameters of the route handler functions as the
// sources in our data flow analysis.
from LikelyRequestParameter p
select p
